// @ts-nocheck
import { useEffect, useRef, useState } from 'react';
import { Canvas, useThree, useFrame } from '@react-three/fiber';
import { OrbitControls, useGLTF } from '@react-three/drei';
import { Select } from 'antd';
import * as THREE from 'three';
import './index.css'
import { useTranslation } from 'react-i18next'

// 定义Model组件的Props类型，包含GLB模型的URL、缩放和位置
interface ModelProps {
	url: string;
	scale?: number;
	position?: [number, number, number];
	selectedAnimation?: string;
	onAnimationsLoad?: (names: string[]) => void;
}
// Model组件：负责加载并渲染GLB模型，同时处理模型动画播放
const Model = ({ url, scale = 1, position = [0, 0, 0], selectedAnimation, onAnimationsLoad }: ModelProps) => {
	// useGLTF钩子加载模型资源，返回模型场景和动画数据
	const { scene, animations } = useGLTF(url);
	// 引用当前模型的Group节点，方便操作
	const ref = useRef<THREE.Group>(null);
	// 用于动画混合器AnimationMixer的引用，管理模型动画
	const mixerRef = useRef<THREE.AnimationMixer | null>(null);
	// 当前播放的动画Action引用，方便控制动画状态
	const actionRef = useRef<THREE.AnimationAction | null>(null);
	const mouthTargetsRef = useRef<{ mesh: any, index: number }[]>([])
	const [isAISpeaking, setIsAISpeaking] = useState(false)
	const [originalAnimation, setOriginalAnimation] = useState<string>('') // 保存原始动画名称
	const [commandOverride, setCommandOverride] = useState(false) // 用户指令优先，跳过AI朗读动画切换
	// 切换动画的函数
	const switchAnimation = (animationName: string) => {
		if (!mixerRef.current || !animations || animations.length === 0) return;

		const clip = animations.find(a => a.name === animationName);
		if (!clip) {
			console.warn(`未找到动画: ${animationName}`);
			return;
		}

		mixerRef.current.stopAllAction();
		const action = mixerRef.current.clipAction(clip);
		actionRef.current = action;
		action.loop = THREE.LoopRepeat;
		action.reset();
		action.setEffectiveWeight(1);
		action.enabled = true;
		action.play();
		console.log('切换到动画:', clip.name);
	}

	// 监听全局 AI 朗读事件
	useEffect(() => {
		const handler = (e: any) => {
			const speaking = !!(e?.detail && e.detail.speaking)
			setIsAISpeaking(speaking)
		}
		window.addEventListener('ai-speech', handler)
		return () => window.removeEventListener('ai-speech', handler)
	}, [])

	// 监听全局头像指令事件，解析并切换对应动画
	useEffect(() => {
		const handleCommand = (e: any) => {
			const raw = (e?.detail?.command ?? '').toString().trim().toLowerCase()
			if (!raw) return
			if (!animations || animations.length === 0) return

			// 指令关键词映射
			const map: Record<string, string[]> = {
				'开心': ['开心','高兴','快乐','愉快','喜悦','兴奋','happy','smile','joy','laugh','cheer','delight','merry'],
				'奔跑': ['奔跑','跑步','跑','疾跑','run','running','sprint'],
				'恰巴舞': ['恰巴舞','恰恰','恰恰舞','cha-cha','chacha','chaba','latin'],
				'沮丧': ['沮丧','伤心','难过','悲伤','丧','sad','depressed','sorrow'],
				'睡觉': ['睡觉','睡','躺下','躺','打盹','sleep','sleeping','lie','lying'],
				'跳舞': ['跳舞','舞蹈','dance','dancing','hiphop','break'],
				'跳跃': ['跳跃','跳','跳起','跳一下','jump','jumping','hop']
			}

			// 根据原始文本确定归一化指令键
			const resolveKey = (): string | null => {
				for (const key of Object.keys(map)) {
					if (map[key].some(k => raw.includes(k.toLowerCase()))) return key
				}
				return null
			}

			const key = resolveKey()
			if (!key) {
				console.warn('未识别的指令:', raw)
				return
			}

			// 在动画名称中查找匹配项
			const findByKeywords = (keywords: string[]) => {
				return animations.find(a => keywords.some(k => a.name.toLowerCase().includes(k.toLowerCase())))
			}

			let target = findByKeywords(map[key])
			// 通用备选：若未找到，尝试常见标识
			if (!target) {
				const commonFallbacks = ['run','dance','jump','sleep','idle','action','anim','happy','sad']
				target = findByKeywords(commonFallbacks)
			}
			// 索引备选
			if (!target) {
				const idxFallbacks = [2,3,5,0,1]
				for (const idx of idxFallbacks) {
					if (animations[idx]) { target = animations[idx]; break }
				}
			}

			if (!target) {
				console.warn('未找到与指令匹配的动画，保持当前动画')
				return
			}

			console.log('收到指令:', raw, '→ 切换动画:', target.name)
			switchAnimation(target.name)
			setCommandOverride(true)
		}

		window.addEventListener('avatar-command', handleCommand)
		return () => window.removeEventListener('avatar-command', handleCommand)
	}, [animations])

	// 监听AI朗读状态变化
	useEffect(() => {
		// console.log('AI朗读状态变化:', { isAISpeaking, selectedAnimation, originalAnimation });
		// console.log('可用动画列表:', animations?.map(a => a.name));

	if (isAISpeaking) {
		if (commandOverride) {
			// 有用户指令时，跳过AI朗读的“开心”覆盖
			return
		}
			// 保存当前动画
			if (selectedAnimation && !originalAnimation) {
				console.log('保存原始动画:', selectedAnimation);
				setOriginalAnimation(selectedAnimation);
			}

			// 扩展开心动画关键词匹配
			const happyKeywords = [
				'happy', 'smile', 'joy', 'cheerful', 'pleased', 'glad', 'excited',
				'laugh', 'grin', 'beam', 'delight', 'elated', 'jubilant', 'merry',
				'开心', '高兴', '快乐', '愉快', '欢乐', '喜悦', '兴奋', '愉悦'
			];

			const availableHappyAnimation = animations?.find(anim =>
				happyKeywords.some(keyword =>
					anim.name.toLowerCase().includes(keyword.toLowerCase())
				)
			);

			if (availableHappyAnimation) {
				console.log('AI朗读开始，切换到开心动画:', availableHappyAnimation.name);
				switchAnimation(availableHappyAnimation.name);
			} else {
				console.warn('未找到开心动画');
				console.log('尝试使用索引方式查找开心动画...');
				// 如果没找到关键词匹配的，尝试使用特定索引或名称
				const fallbackAnimations = animations?.filter((anim, index) => {
					// 可能的开心动画索引或特殊名称
					return index === 2 || index === 3 || index === 5 ||
						anim.name.includes('02') || anim.name.includes('03') ||
						anim.name.includes('Action') || anim.name.includes('Anim');
				});

				if (fallbackAnimations && fallbackAnimations.length > 0) {
					console.log('使用备选动画:', fallbackAnimations[0].name);
					switchAnimation(fallbackAnimations[0].name);
				} else {
					console.warn('完全未找到合适的开心动画，保持当前动画');
				}
			}
		} else {
			// 恢复原始动画
			if (originalAnimation) {
				console.log('AI朗读结束，恢复原始动画:', originalAnimation);
				switchAnimation(originalAnimation);
				setOriginalAnimation('');
			}
		}
	}, [isAISpeaking, animations, selectedAnimation, originalAnimation]);
	// 初始化模型和动画
	useEffect(() => {
		if (!scene) return; // 如果模型场景不存在，则跳过
		// 遍历模型场景中的所有子对象，开启阴影投射和接收
		scene.traverse((child) => {
			if (child instanceof THREE.Mesh) {
				child.castShadow = true;      // 开启投射阴影
				child.receiveShadow = true;   // 开启接收阴影
				// 收集 mouth 变形目标（用于AI朗读时的嘴部动画）
				const dict = (child as any).morphTargetDictionary
				const infl = (child as any).morphTargetInfluences
				if (dict && infl && typeof dict['mouth'] !== 'undefined') {
					const idx = dict['mouth']
					console.log('找到mouth形态键:', idx, child.name)
					mouthTargetsRef.current.push({ mesh: child, index: idx, isMouth: true })
					infl[idx] = 0 // 初始状态设为0
				}
				// 也检查 mouthOpen 作为备选
				if (dict && infl && typeof dict['mouthOpen'] !== 'undefined') {
					const idx = dict['mouthOpen']
					console.log('找到mouthOpen形态键:', idx, child.name)
					mouthTargetsRef.current.push({ mesh: child, index: idx, isMouth: true })
					infl[idx] = 0
				}
			}
		});
		// 创建动画混合器，传入场景对象
		const mixer = new THREE.AnimationMixer(scene);
		mixerRef.current = mixer;
		// 如果模型包含动画，则自动播放并上报名称
		if (animations && animations.length > 0) {
			// console.log('检测到动画:', animations.length, '个');
			// console.log('动画列表:', animations.map((a, i) => `${i}: ${a.name}`));
			// 上报动画名称
			onAnimationsLoad?.(animations.map(a => a.name));
			// 选择选中的或第一个动画播放
			const target = selectedAnimation || animations[0].name;
			const clip = animations.find(a => a.name === target) || animations[0];
			const action = mixer.clipAction(clip);
			actionRef.current = action;
			action.loop = THREE.LoopRepeat;
			action.reset();
			action.setEffectiveWeight(1);
			action.enabled = true;
			action.play();
			// console.log('开始播放动画:', clip.name);
		} else {
			console.warn('模型中没有找到动画');
		}
		// 组件卸载时停止所有动画，防止内存泄漏
		return () => {
			if (mixerRef.current) {
				mixerRef.current.stopAllAction();
			}
		};
	}, [scene, animations]);
	// 选中动画变化时切换
	useEffect(() => {
		if (!mixerRef.current || !animations || animations.length === 0 || !selectedAnimation) return;
		mixerRef.current.stopAllAction();
		const clip = animations.find(a => a.name === selectedAnimation);
		if (!clip) return;
		const action = mixerRef.current.clipAction(clip);
		actionRef.current = action;
		action.loop = THREE.LoopRepeat;
		action.reset();
		action.setEffectiveWeight(1);
		action.enabled = true;
		action.play();
		// console.log('切换到动画:', clip.name);
	}, [selectedAnimation, animations]);
	// 每帧更新动画混合器，保持动画播放
	useFrame((state, delta) => {
		if (mixerRef.current) {
			mixerRef.current.update(delta); // 根据时间差更新动画状态
		}
		if (mouthTargetsRef.current.length) {
			for (const { mesh, index, isMouth } of mouthTargetsRef.current) {
				const infl = (mesh as any).morphTargetInfluences
				if (infl && typeof infl[index] !== 'undefined') {
					if (isMouth) {
						// mouth形态键：AI朗读时在0-1之间切换，朗读结束时返回0
						if (isAISpeaking) {
							// 使用正弦波让切换更自然，每秒约6次切换（提高频率）
							const sineValue = Math.sin(state.clock.elapsedTime * 18) // 12 = 2π * 频率，从6改为12
							infl[index] = sineValue > 0 ? 1 : 0 // 正值时为1，负值时为0
						} else {
							// 朗读结束时返回初始状态0
							infl[index] = 0
						}
						// 添加调试日志
						if (Math.floor(state.clock.elapsedTime * 2) % 2 === 0 && state.clock.elapsedTime % 0.5 < 0.1) {
							// console.log('mouth形态键当前值:', infl[index], 'AI朗读状态:', isAISpeaking)
						}
					}
				}
			}
		}
	});
	// 渲染模型组，设置缩放和位置
	return (
		<group ref={ref} scale={scale} position={position}>
			{/* 直接使用three.js场景作为原生对象挂载 */}
			<primitive object={scene} />
		</group>
	);
}
// GLBViewer组件Props类型，主要包含模型URL，背景色，是否显示控制器，样式类名
interface GLBViewerProps {
	modelUrl: string;
	backgroundColor?: string;
	showControls?: boolean;
	className?: string;
}
// GLBViewer组件：整体的3D模型查看器容器
const GLBViewer = ({
	modelUrl,
	backgroundColor = '#f0f0f0',
	showControls = true,
	className = '',
}: GLBViewerProps) => {
	const { t } = useTranslation()
	// 用于获取Canvas父容器尺寸，方便响应式调整渲染画布大小
	const canvasRef = useRef<HTMLDivElement>(null);
	// 动画列表与选择
	const [animationNames, setAnimationNames] = useState<string[]>([]);
	const [selectedAnimation, setSelectedAnimation] = useState<string>('');
	// 背景样式类名（替代内联背景色）
	const bgClass = backgroundColor === 'transparent' ? 'glb-bg-transparent' : 'glb-bg-default';
	// ResizeHandler组件：监听窗口大小变化，动态更新Three.js相机和渲染器尺寸
	const ResizeHandler = () => {
		const { camera, gl } = useThree();
		useEffect(() => {
			const handleResize = () => {
				if (canvasRef.current) {
					const width = canvasRef.current.clientWidth;
					const height = canvasRef.current.clientHeight;
					// 如果是透视相机，更新长宽比并重新计算投影矩阵
					if ((camera as THREE.PerspectiveCamera).isPerspectiveCamera) {
						(camera as THREE.PerspectiveCamera).aspect = width / height;
						camera.updateProjectionMatrix();
					}
					// 调整WebGL渲染器的尺寸，第三个参数false表示不更新样式尺寸
					gl.setSize(width, height, false);
				}
			};
			window.addEventListener('resize', handleResize);
			handleResize(); // 组件加载时立即调用一次，确保尺寸正确
			// 组件卸载时移除事件监听
			return () => window.removeEventListener('resize', handleResize);
		}, [camera, gl]);
		return null; // 该组件不渲染任何内容，仅执行副作用
	};
	return (
		<div
			ref={canvasRef}
			className={`relative w-full h-full glb-viewer ${bgClass}`}
		>
			{/* 动作选择下拉框（左上角） */}
			{animationNames.length > 0 && (
				<div className='anim-toolbar'>
					<div className='anim-toolbar__title'>{t('choose_action')}</div>
					<Select size="small" value={selectedAnimation} className='anim-toolbar__select' placeholder={t('please_choose_action')} onChange={setSelectedAnimation} options={animationNames.map(n => ({ label: n, value: n }))} />
				</div>
			)}
			{/* Canvas组件：three.js渲染容器 */}
			<Canvas
				shadows // 开启阴影
				camera={{
					position: [0, 5, 55], // 相机初始位置
					fov: 3,               // 视场角，较小以保证视野
				}}
				onCreated={({ gl }) => {
					gl.shadowMap.enabled = true;            // 开启阴影贴图
					gl.shadowMap.type = THREE.PCFSoftShadowMap; // 使用软阴影
				}}
			>
				<ResizeHandler /> {/* 监听窗口尺寸变化 */}
				{/* 场景光照 */}
				<ambientLight intensity={15} /> {/* 环境光，均匀照亮场景 */}
				<directionalLight
					position={[15, 5, 5]}  // 方向光位置
					intensity={1}
					castShadow              // 允许投射阴影
					shadow-mapSize-width={2048}  // 阴影贴图尺寸，高分辨率阴影
					shadow-mapSize-height={2048}
				/>
				{/* 加载并显示模型 */}
				<Model
					url={modelUrl}
					position={[0, -0.8, 0]} // 适当调整模型位置
					selectedAnimation={selectedAnimation}
					onAnimationsLoad={(names) => {
						const filtered = names.slice(8);
						setAnimationNames(filtered);
						if (!selectedAnimation) {
							const defaultName = names[9] || filtered[0];
							if (defaultName) setSelectedAnimation(defaultName);
						}
					}}
				/>
				{/* 可选显示轨道控制器，允许用户旋转、缩放、平移场景 */}
				{showControls && <OrbitControls enablePan enableZoom enableRotate />}
			</Canvas>
		</div>
	);
};
export default GLBViewer;
